﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Windows.Controls;
using System.Xml;
using System.Xml.Serialization;

namespace Deployment.Data.Deploy.Steps.Tasks
{
    [XmlRoot("Task")]
    [Serializable]
    public abstract class AbstractTask : NotifyPropertyChanged
    {
        private int _id = -1;
        private string _description = null;
        private string _TypeDescription = null;
        private bool _AllowParallelExecution;

        private bool _Validated = false;
        private bool _isValidating = false;
        private string _validationFailedMessage = null;

        private bool _isExecuting = false;
        private ExecutionStatus _executionStatus = ExecutionStatus.Idle;

        public static List<AbstractTask> GetAvailableTaskTypes()
        {
            List<AbstractTask> taskTypes = new List<AbstractTask>();
            Assembly asm = Assembly.GetExecutingAssembly();
            FileInfo assemblyFile = new FileInfo(asm.Location);
            DirectoryInfo di = assemblyFile.Directory;
            FileInfo[] files = di.GetFiles("*.dll", SearchOption.AllDirectories);
            foreach (FileInfo file in files)
            {
                Assembly asmFile = Assembly.LoadFrom(file.FullName);
                Type[] types = asmFile.GetTypes();
                for (int i = 0; i < types.Length; i++)
                {
                    if (types[i].IsSubclassOf(typeof(AbstractTask)) && !types[i].IsAbstract)
                    {
                        AbstractTask obj = asmFile.CreateInstance(types[i].FullName) as AbstractTask;
                        if (obj != null)
                            taskTypes.Add(obj);
                    }
                }
            }
            return taskTypes;
        }

        public AbstractTask()
        {
            _TypeDescription = getTaskTypeDescription();
            if (string.IsNullOrEmpty(_TypeDescription))
                throw new ArgumentException("getTaskTypeDescription must return a valid value.");
            _AllowParallelExecution = allowsParallelExecution();
            PropertyChanged += AbstractTask_PropertyChanged;

        }

        void AbstractTask_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (!e.PropertyName.Equals("isContentOk"))
                OnPropertyChanged("isContentOk");
        }

        /// <summary>
        /// Id of the task within the step.  Uniqueness is suggestted but not required.
        /// </summary>
        public int Id
        {
            get { return _id; }
            set
            {
                if (_id != value)
                {
                    _id = value;
                    OnPropertyChanged("Id");
                }
            }
        }

        /// <summary>
        /// Optional value to describe the task being deployed.  If used will be shown/logged during deployment.
        /// </summary>
        public string Description
        {
            get { return _description; }
            set
            {
                if (_description != value)
                {
                    _description = value;
                    OnPropertyChanged("Description");
                }
            }
        }

        /// <summary>
        /// TypeDescription property will be filled with the return value of the getTaskTypeDescription function during initalization.
        /// This value should describ the Task as it will be the value displayed when the user is selecting the type of task to create 
        /// i.e SQL Script Task, SQL Database Project Deployment Task
        /// </summary>
        [XmlIgnore]
        public string TypeDescription
        {
            get { return _TypeDescription; }
        }

        /// <summary>
        /// This will only return true after the ValidateTask method has executed successfully.
        /// </summary>
        [XmlIgnore]
        public bool Validated
        {
            get { return _Validated; }
            private set
            {
                if (_Validated != value)
                {
                    _Validated = value;
                    OnPropertyChanged("Validated");
                }
            }
        }

        /// <summary>
        /// Internaly set to true when the Task is being validated.
        /// </summary>
        [XmlIgnore]
        public bool Validating
        {
            get { return _isValidating; }
            private set
            {
                if (_isValidating != value)
                {
                    _isValidating = value;
                    OnPropertyChanged("Validating");
                }
            }
        }

        [XmlIgnore]
        public string ValidationFailureMessage
        {
            get { return _validationFailedMessage; }
            private set
            {
                if (_validationFailedMessage != value)
                {
                    _validationFailedMessage = value;
                    OnPropertyChanged("ValidationFailureMessage");
                }
            }
        }

        [XmlIgnore]
        public bool AllowParallelExecution
        {
            get { return _AllowParallelExecution; }
        }

        /// <summary>
        /// Returns the current execution status of the task
        /// </summary>
        [XmlIgnore]
        public ExecutionStatus ExecutionStatus
        {
            get { return _executionStatus; }
            private set
            {
                if (_executionStatus != value)
                {
                    _executionStatus = value;
                    OnPropertyChanged("ExecutionStatus");
                }
            }
        }

        private AbstractTaskControl _control = null;
        /// <summary>
        /// Returns the UI control for editing the current Task, with the controls DataContext set to this object.
        /// </summary>
        public AbstractTaskControl TaskControl
        {
            get 
            {
                if (_control == null)
                {
                    _control = createUserControl();
                    _control.Unloaded += control_Unloaded;
                    _control.DataContext = this;
                }
                return _control;
            }
        }

        public bool isContentOk
        {
            get { return check_ContentIsSavable(); }
        }

        void control_Unloaded(object sender, System.Windows.RoutedEventArgs e)
        {
            _control = null;
        }

        /// <summary>
        /// Validate the settings prior to execution
        /// </summary>
        public void ValidateTask(Environment.Environment environment) 
        {
            if (!Validating)
            {
                if (environment == null)
                    throw new ArgumentNullException("environmnet");

                Validating = true;
                BackgroundWorker ValidationWokrer = new BackgroundWorker();
                ValidationWokrer.DoWork += ValidationWorker_DoWork;
                ValidationWokrer.RunWorkerCompleted += ValidationWorker_RunWorkerCompleted;
                ValidationWokrer.RunWorkerAsync(environment);
            }
        }

        private void ValidationWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (!e.Cancelled)
            {
                ValidationResult result = e.Result as ValidationResult;
                if (result != null)
                {
                    this.Validated = result.Successful;
                    this.ValidationFailureMessage = result.Message;
                }
            }
            this.Validating = false;
        }

        private void ValidationWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            Environment.Environment env = e.Argument as Environment.Environment;
            string message = null;
            bool result = performTaskValidation(env, out message);
            e.Result = new ValidationResult(result, message);
        }

       

        /// <summary>
        /// Get the Description of the task to be displayed when creating a new Task
        /// </summary>
        /// <returns>the name to be displayed when selecting a type of task to create.</returns>
        protected abstract string getTaskTypeDescription();

        /// <summary>
        /// Override this method if the task cannot or doesn't support executing in parallel.
        /// </summary>
        /// <returns>true if Parallel exeution is supported, otherwise false.</returns>
        protected virtual bool allowsParallelExecution()
        {
            return true;
        }

        /// <summary>
        /// Read extended data from the Task
        /// </summary>
        /// <param name="reader">the XML node that contains additional info for the task</param>
        protected internal abstract void ReadElement(XmlReader reader);

        /// <summary>
        /// Write extended data for the task
        /// </summary>
        /// <param name="writer">The Task node to write any additional information into.</param>
        protected internal abstract void WriteElement(XmlWriter writer);

        /// <summary>
        /// Validate the task here, this will always execute off the main thread
        /// </summary>
        /// <param name="message">if the validation fails any failure message should be supplied in this variable</param>
        /// <returns>true if validation is successfull</returns>
        protected abstract bool performTaskValidation(Environment.Environment executionEnvironment, out string message);

        /// <summary>
        /// Clone extra properites of the Task
        /// </summary>
        /// <param name="copy">the copy of the task to update.</param>
        protected internal abstract void performClone(ref AbstractTask copy);

        protected internal abstract AbstractTaskControl createUserControl();

        protected internal abstract bool check_ContentIsSavable();

        public AbstractTask Clone()
        {
            AbstractTask clone = Activator.CreateInstance(GetType()) as AbstractTask;
            clone.Id = this.Id;
            clone.Description = this.Description;
            clone.Validated = this.Validated;
            clone.ValidationFailureMessage = this.ValidationFailureMessage;
            clone._AllowParallelExecution = this.AllowParallelExecution;
            performClone(ref clone);
            return clone;
        }

        private class ValidationResult
        {
            private string _message;
            private bool _successful;

            public ValidationResult(bool Successful, string message)
            {
                this._message = message;
                this._successful = Successful;
            }

            internal bool Successful
            {
                get { return _successful; }
            }

            internal string Message
            {
                get { return _message; }
            }
        }
    }
}
